package xm.components
{
	import com.greensock.TweenLite;
	import com.greensock.data.TweenLiteVars;
	import com.greensock.easing.Circ;
	import com.greensock.easing.Cubic;
	
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	
	import xm.components.supportClasses.SkinnableComponent;
	import xm.manager.ConfigManager;
	import xm.manager.StageManager;
	
	
	/**
	 * <br> 클래스 설명 : </br>
	 * @author 김정환
	 */	
	public class ScrollableContainer extends SkinnableComponent
	{
		
		//-----------------------------------------------------------------------------------
		// public vars
		//-----------------------------------------------------------------------------------
		namespace xprimeMobile = "http://www.xprime.co.kr/";
		
		
		/**
		 * 가상의 스크롤 후의 포지션. 
		 */		
		public var scrolledPosY : Number = 0;
		
		
		/**
		 * 스크롤 센스를 위한 임시변수 ( 외부조작하지 말것 ) 
		 */		
		public var __currentPower__ : Number = 0;
		//-----------------------------------------------------------------------------------
		// protected / internal vars
		//-----------------------------------------------------------------------------------
		
		/**
		 * 우측 스크롤 바 버튼 
		 */		
		protected var scrollBtn : Sprite;
		
		/**
		 * 스크롤 사이즈 ( 0~1 ) 
		 */		
		protected var scrollNum : Number = 0;
		
		/**
		 * 마스킹을 위한 shape 
		 */		
		protected var maskClip : Shape;
		
		/**
		 * 사용자 설정에 의한 viewWidth 사용?
		 */		
		protected var useCustomViewWidth : Boolean = false;
		
		/**
		 * 사용자 설정에 의한 viewHeight 사용?
		 */		
		protected var useCustomViewHeight : Boolean = false;
		
		//-----------------------------------------------------------------------------------
		// private vars
		//-----------------------------------------------------------------------------------
		
		
		private var _scrollPT : Point;
		private var _scrollPT2 : Point;
		private var _addPower : Number = 0;
		private var _limitY : Number = 0;
		private var _isMove : Boolean = false;
		private var _minusPowerRate : Number = 5; // 스크롤 영역 넘어선상태에서의 반작용 비율.
		
		private var _scrollColor : uint = 0x333333;
		
		//-----------------------------------------------------------------------------------
		// public functions
		//-----------------------------------------------------------------------------------
		
		/**
		 * constructor 
		 */	
		public function ScrollableContainer()
		{
			super();
		}
		
		
		
		/**
		 * commitProperties
		 */		
		override public function commitProperties():void
		{
			super.commitProperties();
			
		}
		
		
		
		/**
		 * <br>super.updateDisplayList(enforce);를 가장 아래서 구현하여 확장 사용하세요.</br>
		 * updateDisplayList
		 */		
		override public function updateDisplayList( enforce : Boolean = false ):void
		{
			super.updateDisplayList(enforce);
			
			var ltg : Point = this.localToGlobal( new Point( 0, paddingTop ) ), 
				btnMinHeight : Number = 5,
				btnHeight : Number = 0,
				scrollHeight : Number = 0,
				s : Stage = StageManager.getInstance().stage,
				h : Number = this.height;
			
			
			// 1.
			// 우선 mask의 width, height 를 지정한다. 
			// 만약 viewWidth, viewHeight 를 사용자가 지정했으면
			// useCustom 값이 전부 true , 아니면
			// viewWidth, viewHeight 는 availableWidth, availableHeight 가 된다.
			if( useCustomViewWidth == false ) super.viewWidth = availableWidth;
			if( useCustomViewHeight == false ) super.viewHeight = availableHeight;

			maskClip.width = viewWidth;
			maskClip.height = viewHeight;
			
			
			// 2.
			// 스크롤 Height는 패딩을 제외한 구간임.
			scrollHeight = viewHeight-paddingTop-paddingBottom;
			
			
			
			// 3. 
			// y 좌표가 마이너스( - ) 몇까지 올라갈 수 있는지 지정함.
			// 화면이 컨텐츠의 height( h ) 보다 크면 limit은 0 이고 그렇지 않으면
			// 화면크기 - 컨텐츠의 크기가 최대 마이너스( - ) 좌표임.
			_limitY = ( scrollHeight > h ) ? paddingTop : scrollHeight-h+paddingTop;
			
			
			// 4. 스크롤 버튼의 인덱스를 셋팅해주고,
			if( s.contains( scrollBtn ) )
			{
				s.setChildIndex( scrollBtn, s.numChildren - 1 );
			}
			
			// 5. 스크롤 버튼의 x 좌표는 우측 적당한 지점으로..
			scrollBtn.x = viewWidth-scrollBtn.width-2+ltg.x;
			
			
			//6. 일단 버튼의 height을 비율대로 계산한다. 여기서 맥시멈은 scrollHeight이다.
			btnHeight = Math.min( scrollHeight/h * scrollHeight, scrollHeight );
			
			
			// scrolledPosY 가 paddingTop 보다 아래로 내려간다는건, 
			// 스크롤이 모두 끝났는데 아래로 드래그 중 이라는 것.
			if( scrolledPosY > paddingTop )
			{
				btnHeight -= scrolledPosY-paddingTop;
				scrollBtn.height = Math.max( btnHeight, btnMinHeight );
				scrollBtn.y = ltg.y;
			}
			// scrolledPosY 가 _limitY 보다 위로 올라간다는건,
			// 스크롤이 모두 끝났는데 위로 드래그 중 이라는 것.
			else if( scrolledPosY < _limitY )
			{
				btnHeight -= _limitY-scrolledPosY;
				scrollBtn.height = Math.max( btnHeight, btnMinHeight );
				scrollBtn.y = ltg.y+scrollHeight-scrollBtn.height;
			}
			else
			{
				scrollBtn.height = Math.max( btnHeight, btnMinHeight );
				
//				var totalHeight : Number = h-scrollHeight;
//				var btnMovableHeight : Number = scrollHeight-scrollBtn.height;
//				scrollBtn.y = ltg.y - (btnMovableHeight/totalHeight*(scrolledPosY-paddingTop));
				scrollBtn.y = ltg.y - ((scrollHeight-scrollBtn.height)/(h-scrollHeight)*(scrolledPosY-paddingTop));
				
				
			}
			
		}
		
		
		
		
		
		
		//-----------------------------------------------------------------------------------
		// life cycle functions
		//-----------------------------------------------------------------------------------
		
		/**
		 * createChildren
		 */		
		override public function createChildren():void
		{
			super.createChildren();
			
			
			var s : Stage = StageManager.getInstance().stage,
				g : Graphics;
			
			maskClip = new Shape();
			g = maskClip.graphics;
			g.beginFill(0x000000, 0 );
			g.drawRect(0,0,1,1);
			g.endFill();
			
			addChild( maskClip );
			this.mask = maskClip;
			
			this.backgroundColor = 0xffffff;
			this.backgroundAlpha = 1;
			
			scrollBtn = new XM_DEFAULT_SKIN_SCROLLBAR();
			s.addChild( scrollBtn );
			
			var colorTrans : ColorTransform = new ColorTransform();
			colorTrans.color = _scrollColor;
			scrollBtn.transform.colorTransform = colorTrans;
			
			scrollBtn.height = 1;
			scrollBtn.alpha = 0;
		}
		
		/**
		 * createComplete
		 */		
		override public function creationComplete():void
		{
			super.creationComplete();
			
		}
		
		
		/**
		 * add
		 */		
		override public function add():void
		{
			super.add();
			
			var s : Stage = StageManager.getInstance().stage;
			
			this.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
			
			if( s.contains( scrollBtn ) == false ) s.addChild( scrollBtn );
		}
		
		
		
		/**
		 * startAddTransition : add 완료 후 transition 이 있으면 화면전환효과를 실행한다.
		 */		
		override public function startAddTransition():void
		{
			super.startAddTransition();
		}
		
		/**
		 * startAddTransition 완료.
		 */		
		override public function endAddTransition():void
		{
			super.endAddTransition();
		}
		
		
		/**
		 * startRemoveTransition : remove 하기 전 transition 이 있으면 화면전환효과를 실행한다.
		 */		
		override public function startRemoveTransition():void
		{
			super.startRemoveTransition();
			
			var s : Stage = StageManager.getInstance().stage;
			
			this.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
			if( s.contains( scrollBtn ) ) s.removeChild( scrollBtn );
			
		}
		
		
		
		/**
		 * startRemoveTransition 완료.
		 */		
		override public function endRemoveTransition():void
		{
			super.endRemoveTransition();
		}
		
		
		
		/**
		 * remove
		 */		
		override public function remove():void
		{
			super.remove();
			
			var s : Stage = StageManager.getInstance().stage;
			
			this.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
			if( s.contains( scrollBtn ) ) s.removeChild( scrollBtn );
		}
		
		
		/**
		 * destroy
		 */		
		override public function destroy():void
		{
			super.destroy();
			
			var s : Stage;
			
			TweenLite.killTweensOf( scrollBtn );
			TweenLite.killTweensOf( this );
			
			s = StageManager.getInstance().stage;
			this.removeEventListener(Event.ENTER_FRAME, onEnter );
			this.removeEventListener(MouseEvent.MOUSE_DOWN, onDown );
			s.removeEventListener(MouseEvent.MOUSE_MOVE, onMove );
			s.removeEventListener(MouseEvent.MOUSE_UP, onUp);
			s.removeEventListener(Event.MOUSE_LEAVE, onUp );
			
			
			if( scrollBtn )
			{
				if( scrollBtn.parent ) scrollBtn.parent.removeChild( scrollBtn );
				scrollBtn = null;
			}
			
			if( maskClip )
			{
				if( maskClip.parent ) maskClip.parent.removeChild( maskClip );
				maskClip = null;
			}
			
			scrollNum = NaN;
			useCustomViewHeight = false;
			useCustomViewWidth = false;
			_scrollPT = null;
			_scrollPT2 = null;
			_addPower = NaN;
			_limitY = NaN;
			_isMove = false;
			_minusPowerRate = NaN;
			
		}
		
		//-----------------------------------------------------------------------------------
		// setter / getter
		//-----------------------------------------------------------------------------------
		/**
		 * <br>사용자 설정에 의한 viewWidth 사용여부 설정</br>
		 * @private
		 */
		override public function set viewWidth(value:Number):void
		{
			useCustomViewWidth = true;
			super.viewWidth = value;
		}
		
		
		/**
		 * <br>사용자 설정에 의한 viewHeight 사용여부 설정</br>
		 * @private
		 */
		override public function set viewHeight(value:Number):void
		{
			useCustomViewHeight = true;
			super.viewHeight = value;
		}
		
		
		/**
		 * @param color
		 */		
		public function set scrollColor( color : uint ):void
		{
			var colorTrans : ColorTransform = new ColorTransform();
			colorTrans.color = color;
			
			_scrollColor = color;
			
			if( scrollBtn != null )
			{
				scrollBtn.transform.colorTransform = colorTrans;
				scrollBtn.alpha = 0;
			}
		}
		
		//-----------------------------------------------------------------------------------
		// protected functions
		//-----------------------------------------------------------------------------------
		
		
		
		//-----------------------------------------------------------------------------------
		// internal functions
		//-----------------------------------------------------------------------------------
		
		
		
		
		//-----------------------------------------------------------------------------------
		// private functions
		//-----------------------------------------------------------------------------------
		
		
		// 트윈 업데이트중.
		private function onTweenUpdate():void
		{
			if( checkOutOfBound() )
			{
				onTweenReadyToBack();
			}
			
			
			// 쩜좌표 제거.
			scrolledPosY = Math.round( scrolledPosY );
			
			invalidateDisplayList();
		}
		
		// 스크롤 영역인지 체크.
		private function checkOutOfBound():Boolean
		{
			// 스크롤 영역을 벗어남.
			// 스크롤 포지션이 너무 아래로 쳐졌거나, 너무 위로 갔을경우 원복, 
			if( scrolledPosY > paddingTop || scrolledPosY < _limitY )
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		
		// 1. 스크롤 트윈을 원상복구 시키기 전 잔여힘에 의한효과.
		private function onTweenReadyToBack():void
		{
			TweenLite.killTweensOf( this );
			TweenLite.to( this, Math.min( 0.1, __currentPower__/30 ), {scrolledPosY:scrolledPosY+__currentPower__, ease:Cubic.easeOut, onUpdate:invalidateDisplayList, onComplete:onTweenInitPos} );
			__currentPower__ = 0;
		}
		
		// 2. 스크롤 트윈을 원상복구 시킴.
		private function onTweenInitPos():void
		{
			if( scrolledPosY > paddingTop || this.height <= viewHeight-paddingTop-paddingBottom)
			{
				TweenLite.killTweensOf( this );
				TweenLite.to( this, 0.3, {scrolledPosY:paddingTop, ease:Cubic.easeOut, onUpdate:invalidateDisplayList, onComplete:hideScrollBtn} );
			}
			else if( scrolledPosY < _limitY )
			{
				TweenLite.killTweensOf( this );
				TweenLite.to( this, 0.3, {scrolledPosY:_limitY, ease:Cubic.easeOut, onUpdate:invalidateDisplayList, onComplete:hideScrollBtn} );
			}
		}
		
		// 스크롤 사라짐.
		private function hideScrollBtn():void
		{
			TweenLite.killTweensOf( scrollBtn );
			TweenLite.to( scrollBtn, 0.3, {alpha:0} );
		}
		
		
		//-----------------------------------------------------------------------------------
		// event handler
		//-----------------------------------------------------------------------------------
		
		
		/**
		 * <br>터치 시작. 각종 이벤트 걸기 및 트위너를 모두 제거한다.</br>
		 * @param e MouseDown
		 */		
		protected function onDown(e:Event):void
		{
			var s : Stage = StageManager.getInstance().stage;
			
			TweenLite.killTweensOf( this );
			
			s.addEventListener(MouseEvent.MOUSE_MOVE, onMove );
			s.addEventListener(MouseEvent.MOUSE_UP, onUp);
			s.addEventListener(Event.MOUSE_LEAVE, onUp );
			_addPower = 0;
			_scrollPT = new Point( s.mouseX, s.mouseY );
			_scrollPT2 = new Point( s.mouseX, s.mouseY );
		}
		
		/**
		 * <br>드래그 중. 최소값만큼 움직여줘야 EnterFrame 이 작동함.</br>
		 * @param e
		 */		
		protected function onMove(e:Event):void
		{
			// 5만큼은 움직여야지 무브로 간주한다.
			if( Math.abs( StageManager.getInstance().stage.mouseY-_scrollPT.y ) > ConfigManager.FLICK_SENSE_NUM ) 
			{
				_isMove = true;
				// 일단 무브가 시작되면 클릭은 발생하지 않음.
				this.addEventListener(Event.ENTER_FRAME, onEnter );
			}
		}
		
		/**
		 * <br>터치중 드래그한다면, 과부화를 줄이기 위해 fremeRate 에 맞게 화면을 렌더링 한다.</br>
		 * <br>만약 움직이다가 멈춰있을 경우를 위해 파워를 늘리는 공식 + 파워를 비율로 줄이는 공식 2가지를 적용함.</br>
		 * @param e
		 */		
		protected function onEnter(e:Event):void
		{
			var s : Stage = StageManager.getInstance().stage,
				currentValue : Number = s.mouseY-_scrollPT2.y;
			
			// 최후의 이동역역을 가지고 파워를 비율로 증가시키는 로직 ( 추천 )
			_addPower = ( s.mouseY-_scrollPT2.y );
			_addPower = ( _addPower > 0 ) ? Math.floor( _addPower ) : Math.round( _addPower );

			// 드래그하는동안 파워를 감소시킨다.
			__currentPower__ = __currentPower__-__currentPower__/_minusPowerRate;
			
			if( currentValue == 0 ) return;
			
			if( checkOutOfBound() )
			{
				scrolledPosY += Math.floor( currentValue/3 ); 
			}
			else
			{
				scrolledPosY += Math.floor( currentValue ); 
			}
			
			
			_scrollPT2 = _scrollPT;
			_scrollPT = new Point( s.mouseX, s.mouseY );
			
			if( scrollBtn.alpha < 1 )
			{
				TweenLite.killTweensOf( scrollBtn );
				TweenLite.to( scrollBtn, 0.3, {alpha:1} );
			}
			
			// 쩜좌표 제거.
			scrolledPosY = Math.round( scrolledPosY );
			
			this.invalidateDisplayList();
		}
		
		/**
		 * <br>터치가 끝났을 때 __currentPower__ 를 가지고 얼마나 스크롤 할건지 결정한다.</br>
		 * @param e MouseUp
		 */		
		protected function onUp(e:Event):void
		{
			var s : Stage = StageManager.getInstance().stage,
				targetY : Number,
				tweenVars : TweenLiteVars;
			
			__currentPower__ +=_addPower; 
			
			targetY = scrolledPosY+__currentPower__*10;
			
			removeEventListener(Event.ENTER_FRAME, onEnter );
			s.removeEventListener(MouseEvent.MOUSE_MOVE, onMove );
			s.removeEventListener(MouseEvent.MOUSE_UP, onUp);
			s.removeEventListener(Event.MOUSE_LEAVE, onUp );
			
			
			tweenVars = new TweenLiteVars();
			tweenVars.prop( "scrolledPosY", targetY );
			tweenVars.prop( "__currentPower__", 0 );
			tweenVars.onUpdate( onTweenUpdate );
			tweenVars.onComplete( hideScrollBtn );
			tweenVars.ease( Circ.easeOut );
			
			// duration을 구하는 2차방정식
//			var duration : Number = Math.abs( Math.pow(__currentPower__,2)/(__currentPower__*35) );
//			trace(" ==> ScrollableContainer onUp : " + duration );
			
			// 화면 사용가능높이보다, 컨텐츠가 길경우에만 영역벗어남을 체크한다.
			if( this.height > viewHeight-paddingTop-paddingBottom && checkOutOfBound() )
			{
				// 영역벗어나면 원위치로 하는 탄성적용
				onTweenReadyToBack();
			}
			// 그게아닐경우 스크롤한다
			else if( this.height <= viewHeight-paddingTop-paddingBottom && 
				( _isMove == true ||
					( scrolledPosY == paddingTop || scrolledPosY == _limitY ) == false )
			)
			{
				TweenLite.to( this, 1.4, tweenVars ); // duration 1초로 고정.
			}
			else if( _isMove == true && Math.abs( __currentPower__ ) > 0.1 )
			{
				TweenLite.to( this, 1.4, tweenVars ); // duration 1초로 고정.
			}
			else
			{
				hideScrollBtn();
			}
			
			
			_isMove = false;
		}
		
		
		
		
		
		
	}
}